Skip to content

feat: add template-no-duplicate-form-names#2762

Draft
johanrd wants to merge 5 commits intoember-cli:masterfrom
johanrd:html-validate/template-no-duplicate-form-names
Draft

feat: add template-no-duplicate-form-names#2762
johanrd wants to merge 5 commits intoember-cli:masterfrom
johanrd:html-validate/template-no-duplicate-form-names

Conversation

@johanrd
Copy link
Copy Markdown
Contributor

@johanrd johanrd commented Apr 27, 2026

Note

This is part of a series where Claude has audited eslint-plugin-ember against jsx-a11y, vuejs-accessibility, angular-eslint, lit-a11y and html-validate, ember-template-lint, and the HTML and WCAG specs.

See html-validate form-dup-name for the peer rule concept.

Adds template-no-duplicate-form-names: flags two form controls sharing a name attribute within the same <form> (or template root if no enclosing form). Radio groups and repeated submit-type controls are explicitly allowed to share a name.

Premise

Per HTML spec §4.10.22.4 — Constructing the entry list, the serializer iterates submittable form controls and emits one entry per contributing control. Two regular controls with the same name both contribute, and server-side frameworks that expect a single value will silently see one — usually not the one the author intended.

The spec distinguishes three categories for name-collision purposes:

  • Non-submitting<input type="button"|"reset">, <button type="button"|"reset">. These do not contribute to the form-data entry list at all; their name can't collide with anything.
  • Activation-only<input type="radio">, submit-like controls (<input type="submit">, <button type="submit">). Sharing a name with same-type siblings is the legitimate pattern: a radio group, or multiple submits distinguished by value.
  • Regular — everything else. Two regular controls with the same name is a real collision.

Flags

<form>
  <input name='email' />
  <input name='email' />              {{! duplicate }}
</form>

<form>
  <input type='text' name='field' />
  <textarea name='field'></textarea>  {{! different tag, same name }}
</form>

<form>
  <input type='radio' name='a' />
  <input type='submit' name='a' />   {{! both in shared set, but different types }}
</form>

Allows

{{! Radio group — legitimate shared name }}
<form>
  <input type='radio' name='color' value='red' />
  <input type='radio' name='color' value='blue' />
</form>

{{! Multiple submit buttons with same name — legitimate }}
<form>
  <button type='submit' name='action' value='save'>Save</button>
  <button type='submit' name='action' value='publish'>Publish</button>
</form>

{{! Disabled controls don't submit }}
<form>
  <input name='a' />
  <input name='a' disabled />
</form>

{{! Dynamic name — skipped }}
<form>
  <input name={{this.fieldName}} />
  <input name='email' />
</form>

{{! Mutually exclusive branches never render together }}
<form>
  {{#if @edit}}<input name='x' />{{/if}}
  {{#unless @edit}}<input name='x' />{{/unless}}
</form>

Scope

This is a v1 implementation. Not ported from html-validate: name[] array syntax; the <input type="hidden"> + <input type="checkbox"> default-value pattern. Both are rarely seen in Ember apps; can be added on demand.

Opt-in: not added to any preset config.

Prior art

Plugin Equivalent
html-validate form-dup-name — full implementation with name[] array handling and a complete form-associated-element registry
jsx-a11y
vuejs-accessibility
lit-a11y
@angular-eslint/template no-duplicate-attributes — different concern (same attribute twice on one element, not same name across controls)

// — `disabled="false"` (static string), `disabled={{"false"}}` (string-
// literal mustache) — still mean disabled per HTML boolean-attribute
// semantics: presence = disabled regardless of value content.
function isDisabled(node) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this? wouldn't we want to disallow all duplicate names?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, agree, removed now

@johanrd johanrd marked this pull request as draft April 30, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants